---
title: Programmatic Control
description: Controlling the state machines programmatically
---

In some cases, you may want to control the state or context values of a machine
programmatically via its `props` or based on certain conditions. This is
typically known as "controlling" the components.

Zag provides a number of ways to control the state of a machine
programmatically.

## Setting initial context

All machines support setting the controlled and uncontrolled values for context
properties. For example:

- `defaultOpen` and `open` for controlling the open state of disclosure
  components
- `defaultValue` and `value` for controlling the value of input machines

For example, if you want an accordion to start with a specific selected value.
Here's how to achieve that:

```tsx
const service = useMachine(accordion.machine, {
  defaultValue: ["item-1"],
})
```

## Controlled Usage

You can pass the context value to the `useMachine` hook directly and provide the
`onValueChange` callback to react to the changes.

```jsx
const service = useMachine(accordion.machine, {
  value: props.value,
  onValueChange(details) {
    console.log(details)
  },
})
```

## Reactive Context

Different frameworks handle reactivity differently when working with Zag
machines. Here's how to ensure your machine context stays reactive to prop
changes.

### React

React props are natively reactive with Zag machines. Simply pass them directly
to `useMachine`

```tsx
function Checkbox(props) {
  const service = useMachine(checkbox.machine, {
    checked: props.checked,
    onCheckedChange: props.onCheckedChange,
  })

  const api = checkbox.connect(service, normalizeProps)
  return <label {...api.getRootProps()}>Toggle</label>
}
```

### Vue

Use a computed ref or a function that returns the context to make it reactive

```html
<script setup lang="ts">
  import { computed } from "vue"

  const service = useMachine(
    checkbox.machine,
    computed(() => ({
      checked: props.checked,
      onCheckedChange: props.onCheckedChange,
    })),
  )
</script>
```

### Solid

Use a function that returns the context to make it reactive

```tsx
function Checkbox(props) {
  const service = useMachine(checkbox.machine, () => ({
    checked: props.checked,
    onCheckedChange: props.onCheckedChange,
  }))

  const api = createMemo(() => checkbox.connect(service, normalizeProps))
  return <label {...api().getRootProps()}>Toggle</label>
}
```

### Svelte

To make props reactive in Svelte, pass a function that returns the context

```svelte
<script lang="ts">
  let checked = $state(machineProps.checked ?? false)

  const service = useMachine(checkbox.machine, () => ({
    ...machineProps,
    checked,
    onCheckedChange(details) {
      checked = details.checked
      machineProps.onCheckedChange?.(details)
    },
  }))

  const api = $derived(checkbox.connect(service, normalizeProps))
</script>

<label {...api.getRootProps()}>Toggle</label>
```

**Why this works:**

1. **Reactive Context**: The function re-evaluates whenever dependencies change,
   ensuring the machine always has current prop values
2. **State Preservation**: The machine's internal state remains intact while
   context gets updated
3. **Framework Integration**: This pattern aligns with the framework's
   reactivity model

This approach ensures single-directional data flow while maintaining reactivity.

## Using API methods

The `connect` method of the machines provide helpful methods (APIs) to change
the machine state or update its context.

> This approach is the recommended approach to programmatically update a
> machine.

Let's say we'd like to change the expanded accordion item in an accordion group.
Here's how to do that:

```jsx
function Accordion() {
  // 1. Bind the machine in your framework
  const service = useMachine(accordion.machine)

  // 2. Call the connect function
  const api = accordion.connect(service)

  // 3. Use exposed methods
  api.setValue("item-1")

  return (...)
}
```
